QtWidgets import QApplication  ,QWidget 
from PyQt5.QtGui import   QPainter ,QPixmap
from PyQt5.QtCore import Qt , QPoint


class Winform(QWidget):
    def __init__(self, parent=None):
        super(Winform, self).__init__(parent)
        self.setWindowTitle("双缓冲绘图示意")
        self.resize(600, 500)
        self.pix =  QPixmap(400, 400) # 400*400画布
        self.pix.fill(Qt.white) # 白色填充
        self.temp = QPixmap() # 辅助画布
        self.lastPoint =  QPoint() # 起点
        self.endPoint =  QPoint() # 终点
    
    def paintEvent(self, event):
        """
        重载绘制事件。
        将self.pix中的内容复制到缓存中，在缓存上绘图。
        """
        x = self.lastPoint.x()
        y = self.lastPoint.y()
        w = self.endPoint.x() - x
        h = self.endPoint.y() - y
        
        self.temp = self.pix.copy() # 如果直接赋值，两者的内存是相同的，因此这里需要调用copy。
        pp = QPainter(self.temp)
        pp.drawRect(x,y,w,h)
        painter = QPainter(self)
        painter.drawPixmap(0, 0, self.temp)  
    
    
    def mousePressEvent(self, event):
        """
        按下鼠标左键后，将当前位置存储到起点中。
        """
        if event.button() == Qt.LeftButton :
            self.lastPoint = event.pos()
    
    
    def mouseMoveEvent(self, event):
        """
        鼠标左键被按下且在滑动中，调用绘图函数，更新画布内容。
        """
        if event.buttons() and Qt.LeftButton: # 这里的写法非常重要
            self.endPoint = event.pos()
            self.update()
    
    def mouseReleaseEvent(self, event):
        """
        松开鼠标左键后，将当前位置存储到终点中；绘制图案，并将缓存中的内容更新到self.pix中。
        """
        self.endPoint = event.pos()
        self.update()
        self.pix = self.temp.copy()


if __name__ == "__main__":  
    app = QApplication(sys.argv) 
    form = Winform()
    form.show()
    sys.exit(app.exec_())